From 6349b4ff570a6ab876207e6cb27a74f4e1a1c6a0 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 12 Oct 2022 12:47:43 +0200 Subject: [PATCH] [jOOQ/jOOQ#13375] Add support for unnesting collections of expressions with DSL::unnest --- .../java/org/jooq/impl/ArrayOfValues.java | 188 ++++++++++++++++++ .../main/java/org/jooq/impl/ArrayTable.java | 13 +- .../org/jooq/impl/ArrayTableEmulation.java | 2 +- jOOQ/src/main/java/org/jooq/impl/DSL.java | 12 +- 4 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/impl/ArrayOfValues.java diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayOfValues.java b/jOOQ/src/main/java/org/jooq/impl/ArrayOfValues.java new file mode 100644 index 0000000000..120e104d3d --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayOfValues.java @@ -0,0 +1,188 @@ +/* + * 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 static org.jooq.impl.Keywords.K_ARRAY; +import static org.jooq.impl.Keywords.K_UNNEST; +import static org.jooq.impl.Names.N_ARRAY_TABLE; +import static org.jooq.impl.Names.N_COLUMN_VALUE; +import static org.jooq.impl.Tools.isEmpty; + +import org.jooq.Context; +import org.jooq.Field; +import org.jooq.Name; +import org.jooq.Param; +import org.jooq.Record; +import org.jooq.Table; +import org.jooq.TableOptions; +import org.jooq.impl.QOM.UNotYetImplemented; +import org.jooq.impl.QOM.UTransient; + +/** + * An unnested array + * + * @author Lukas Eder + */ +final class ArrayOfValues extends AbstractTable implements UNotYetImplemented { + + private final Field[] array; + private final FieldsImpl field; + private final Name alias; + private final Name[] fieldAliases; + + ArrayOfValues(Field[] array) { + this(array, N_ARRAY_TABLE); + } + + ArrayOfValues(Field[] array, Name alias) { + this(array, alias, null); + } + + ArrayOfValues(Field[] array, Name alias, Name[] fieldAliases) { + super(TableOptions.expression(), alias); + + Class arrayType = !isEmpty(array) ? array[0].getType() : Object.class; + + this.array = array; + this.alias = alias; + this.fieldAliases = isEmpty(fieldAliases) ? new Name[] { N_COLUMN_VALUE } : fieldAliases; + this.field = ArrayTable.init(arrayType, this.alias, this.fieldAliases[0]); + } + + @Override + public final Class getRecordType() { + return RecordImplN.class; + } + + @Override + public final Table as(Name as) { + return new ArrayOfValues(array, as); + } + + @Override + public final Table as(Name as, Name... fields) { + return new ArrayOfValues(array, as, fields); + } + + @Override + public final boolean declaresTables() { + + // Always true, because unnested tables are always aliased + return true; + } + + @Override + public final void accept(Context ctx) { + switch (ctx.family()) { + + + + case CUBRID: + + case DERBY: + + case FIREBIRD: + + case IGNITE: + + + case MARIADB: + + case MYSQL: + + + + + case SQLITE: + + + + + ctx.visit(new ArrayTableEmulation(array).as(alias, fieldAliases)); + break; + + default: + ctx.visit(new PostgresHSQLDBTable().as(alias, fieldAliases)); + break; + } + } + + private class PostgresHSQLDBTable extends DialectArrayTable { + + @Override + public final void accept(Context ctx) { + ctx.visit(K_UNNEST).sql('(').visit(K_ARRAY).sql('[').visit(QueryPartListView.wrap(array)).sql(']').sql(")"); + } + } + + private abstract class DialectArrayTable extends AbstractTable implements UTransient { + + DialectArrayTable() { + super(TableOptions.expression(), alias); + } + + @Override + public final Class getRecordType() { + return RecordImplN.class; + } + + @Override + final FieldsImpl fields0() { + return ArrayOfValues.this.fields0(); + } + } + + @Override + final FieldsImpl fields0() { + return field; + } + + // ------------------------------------------------------------------------- + // XXX: Query Object Model + // ------------------------------------------------------------------------- + + @Override + public final Table $aliased() { + return new ArrayOfValues(array); + } + + @Override + public final Name $alias() { + return alias; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayTable.java b/jOOQ/src/main/java/org/jooq/impl/ArrayTable.java index 5b881bbab1..4b456ab9a3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ArrayTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayTable.java @@ -42,11 +42,13 @@ import static org.jooq.impl.Keywords.K_TABLE; import static org.jooq.impl.Keywords.K_UNNEST; import static org.jooq.impl.Names.N_ARRAY_TABLE; import static org.jooq.impl.Names.N_COLUMN_VALUE; +import static org.jooq.impl.Tools.isEmpty; import static org.jooq.impl.Tools.map; // ... import org.jooq.Configuration; import org.jooq.Context; +import org.jooq.DataType; import org.jooq.Field; import org.jooq.Name; import org.jooq.Param; @@ -114,7 +116,16 @@ final class ArrayTable extends AbstractTable implements UNotYetImplement this.field = init(arrayType, this.alias, this.fieldAliases[0]); } - private static final FieldsImpl init(Class arrayType, Name alias, Name fieldAlias) { + @SuppressWarnings("removal") + static final DataType componentDataType(Object[] array) { + if (!isEmpty(array) && array[0] instanceof Field f) { + return f.getDataType(); + } + else + return DSL.getDataType(array.getClass().getComponentType()); + } + + static final FieldsImpl init(Class arrayType, Name alias, Name fieldAlias) { // [#1114] [#7863] VARRAY/TABLE of OBJECT have more than one field if (Record.class.isAssignableFrom(arrayType)) { diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayTableEmulation.java b/jOOQ/src/main/java/org/jooq/impl/ArrayTableEmulation.java index 4e5a49739a..328a0460e2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ArrayTableEmulation.java +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayTableEmulation.java @@ -83,7 +83,7 @@ final class ArrayTableEmulation extends AbstractTable implements UTransi this.array = array; this.alias = alias; this.fieldAlias = fieldAlias == null ? N_COLUMN_VALUE : fieldAlias; - this.field = new FieldsImpl<>(DSL.field(name(alias.last(), this.fieldAlias.last()), DSL.getDataType(array.getClass().getComponentType()))); + this.field = new FieldsImpl<>(DSL.field(name(alias.last(), this.fieldAlias.last()), ArrayTable.componentDataType(array))); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 11f334cdc6..edba59d32e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -123,6 +123,7 @@ import static org.jooq.impl.SQLDataType.VARCHAR; import static org.jooq.impl.Tools.EMPTY_FIELD; import static org.jooq.impl.Tools.combine; import static org.jooq.impl.Tools.configuration; +import static org.jooq.impl.Tools.isEmpty; import static org.jooq.impl.Tools.map; import static org.jooq.tools.StringUtils.isEmpty; @@ -11188,7 +11189,7 @@ public class DSL { @NotNull @Support public static Table table(Collection list) { - return table(list.toArray()); + return unnest(list); } /** @@ -11199,7 +11200,7 @@ public class DSL { @NotNull @Support public static Table table(Object[] array) { - return unnest0(val(array)); + return unnest(array); } @@ -11224,7 +11225,7 @@ public class DSL { @NotNull @Support({ H2, HSQLDB, POSTGRES }) public static Table table(Field cursor) { - return unnest0(cursor); + return unnest(cursor); } /** @@ -11260,7 +11261,10 @@ public class DSL { @NotNull @Support public static Table unnest(Object[] array) { - return unnest0(val(array)); + if (!isEmpty(array) && array[0] instanceof Field) + return new ArrayOfValues(Tools.fieldsArray(array)); + else + return unnest0(val(array)); }