From 981411d2c1ae41f85e76d2ca85add97c33292db0 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Mon, 21 Feb 2022 15:27:44 +0100 Subject: [PATCH] [jOOQ/jOOQ#4727] Let Table extend SelectField --- .../org/jooq/impl/AbstractRowAsField.java | 196 ++++++++++++++++++ .../main/java/org/jooq/impl/RowAsField.java | 155 ++++++++++++++ .../main/java/org/jooq/impl/TableAsField.java | 150 ++++++++++++++ 3 files changed, 501 insertions(+) create mode 100644 jOOQ/src/main/java/org/jooq/impl/AbstractRowAsField.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/RowAsField.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/TableAsField.java diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRowAsField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRowAsField.java new file mode 100644 index 0000000000..9d365d5dc0 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRowAsField.java @@ -0,0 +1,196 @@ +/* + * 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 java.lang.Boolean.TRUE; +import static org.jooq.impl.DSL.jsonArray; +import static org.jooq.impl.DSL.jsonObject; +import static org.jooq.impl.DSL.jsonbArray; +import static org.jooq.impl.DSL.jsonbObject; +import static org.jooq.impl.DSL.select; +import static org.jooq.impl.DSL.xmlelement; +import static org.jooq.impl.Multiset.returningClob; +import static org.jooq.impl.Names.N_RECORD; +import static org.jooq.impl.Tools.emulateMultiset; +import static org.jooq.impl.Tools.fieldNameString; +import static org.jooq.impl.Tools.map; +import static org.jooq.impl.Tools.row0; +import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONTENT; + +import java.util.function.Consumer; + +import org.jooq.Configuration; +import org.jooq.Context; +import org.jooq.DataType; +import org.jooq.Field; +import org.jooq.Fields; +import org.jooq.Name; +import org.jooq.Record; +import org.jooq.Row; + +/** + * @author Lukas Eder + */ +abstract class AbstractRowAsField extends AbstractField { + + AbstractRowAsField(Name name, DataType type) { + super(name, type); + } + + abstract Fields fields0(); + + @SuppressWarnings("unchecked") + final AbstractRow emulatedFields(Configuration configuration) { + return (AbstractRow) row0(map(fields0().fields(), x -> x.as(getUnqualifiedName().unquotedName() + configuration.settings().getNamePathSeparator() + x.getName()), Field[]::new)); + } + + @Override + final int projectionSize() { + int result = 0; + + for (Field field : fields0().fields()) + result += ((AbstractField) field).projectionSize(); + + return result; + } + + @Override + public final boolean declaresFields() { + return true; + } + + @Override + public final void accept(Context ctx) { + + // [#12021] If a RowField is nested somewhere in MULTISET, we must apply + // the MULTISET emulation as well, here + if (TRUE.equals(ctx.data(DATA_MULTISET_CONTENT))) + acceptMultisetContent(ctx, getDataType().getRow(), this, this::acceptDefault); + else + acceptDefault(ctx); + } + + static void acceptMultisetContent(Context ctx, Row row, Field field, Consumer> acceptDefault) { + Name alias = field.getUnqualifiedName(); + + switch (emulateMultiset(ctx.configuration())) { + case JSON: + switch (ctx.family()) { + + + + + + + + + + + + + + + + + default: + ctx.visit(alias(ctx, alias, returningClob(ctx, jsonArray(row.fields()).nullOnNull()))); + break; + } + + break; + + case JSONB: + switch (ctx.family()) { + + + + + + + + + + + + + + + + + default: + ctx.visit(alias(ctx, alias, returningClob(ctx, jsonbArray(row.fields()).nullOnNull()))); + break; + } + + break; + + case XML: + switch (ctx.family()) { + + + + + + + + + + default: + ctx.visit(alias(ctx, alias, xmlelement(N_RECORD, + map(row.fields(), (f, i) -> xmlelement(fieldNameString(i), f))) + )); + + break; + } + + break; + + // case ARRAY: + case NATIVE: + default: + acceptDefault.accept(ctx); + break; + } + } + + private static final Field alias(Context ctx, Name alias, Field field) { + return ctx.declareFields() ? field.as(alias) : field; + } + + abstract void acceptDefault(Context ctx); +} diff --git a/jOOQ/src/main/java/org/jooq/impl/RowAsField.java b/jOOQ/src/main/java/org/jooq/impl/RowAsField.java new file mode 100644 index 0000000000..e29f509df0 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/RowAsField.java @@ -0,0 +1,155 @@ +/* + * 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.SQLDialect.CUBRID; +// ... +import static org.jooq.SQLDialect.DERBY; +// ... +import static org.jooq.SQLDialect.FIREBIRD; +import static org.jooq.SQLDialect.H2; +// ... +import static org.jooq.SQLDialect.HSQLDB; +import static org.jooq.SQLDialect.IGNITE; +// ... +// ... +// ... +import static org.jooq.SQLDialect.MARIADB; +// ... +import static org.jooq.SQLDialect.MYSQL; +// ... +// ... +// ... +// ... +import static org.jooq.SQLDialect.SQLITE; +// ... +// ... +// ... +// ... +import static org.jooq.impl.Keywords.K_ROW; +import static org.jooq.impl.Names.N_ROW; +import static org.jooq.impl.Tools.BooleanDataKey.DATA_LIST_ALREADY_INDENTED; + +import java.util.Set; + +import org.jooq.Context; +import org.jooq.Field; +import org.jooq.Name; +import org.jooq.QueryPart; +import org.jooq.Record; +// ... +import org.jooq.Row; +import org.jooq.SQLDialect; +// ... + +/** + * @author Lukas Eder + */ +final class RowAsField extends AbstractRowAsField implements QOM.RowAsField { + + static final Set NO_NATIVE_SUPPORT = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, IGNITE, MARIADB, MYSQL, SQLITE); + + final ROW row; + + RowAsField(ROW row) { + this(row, N_ROW); + } + + RowAsField(ROW row, Name as) { + super(as, new RecordDataType<>(row)); + + this.row = row; + } + + @Override + final ROW fields0() { + return row; + } + + @Override + final void acceptDefault(Context ctx) { + if (NO_NATIVE_SUPPORT.contains(ctx.dialect())) + ctx.data(DATA_LIST_ALREADY_INDENTED, true, c -> c.visit(new SelectFieldList<>(emulatedFields(ctx.configuration()).fields.fields))); + + + + + + + + + + + // [#11812] RowField is mainly used for projections, in case of which an + // explicit ROW keyword helps disambiguate (1) from ROW(1) + else + ctx.visit(K_ROW).sql(' ').visit(row); + } + + @Override + public Field as(Name alias) { + return new RowAsField<>(row, alias); + } + + // ------------------------------------------------------------------------- + // XXX: Query Object Model + // ------------------------------------------------------------------------- + + @Override + public final Row $row() { + return row; + } + + + + + + + + + + + + + + +} diff --git a/jOOQ/src/main/java/org/jooq/impl/TableAsField.java b/jOOQ/src/main/java/org/jooq/impl/TableAsField.java new file mode 100644 index 0000000000..28982141d6 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/TableAsField.java @@ -0,0 +1,150 @@ +/* + * 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.SQLDialect.CUBRID; +// ... +import static org.jooq.SQLDialect.DERBY; +// ... +import static org.jooq.SQLDialect.FIREBIRD; +import static org.jooq.SQLDialect.H2; +// ... +import static org.jooq.SQLDialect.HSQLDB; +import static org.jooq.SQLDialect.IGNITE; +// ... +// ... +import static org.jooq.SQLDialect.MARIADB; +// ... +import static org.jooq.SQLDialect.MYSQL; +// ... +// ... +// ... +// ... +import static org.jooq.SQLDialect.SQLITE; +// ... +// ... +// ... +// ... +import static org.jooq.impl.Tools.BooleanDataKey.DATA_LIST_ALREADY_INDENTED; + +import java.util.Set; + +import org.jooq.Context; +import org.jooq.Field; +import org.jooq.Name; +import org.jooq.QueryPart; +import org.jooq.Record; +// ... +import org.jooq.SQLDialect; +import org.jooq.Table; +// ... + +/** + * @author Lukas Eder + */ +final class TableAsField extends AbstractRowAsField implements QOM.TableAsField { + + static final Set NO_NATIVE_SUPPORT = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, IGNITE, MARIADB, MYSQL, SQLITE); + + final Table table; + + TableAsField(Table table) { + this(table, table.getQualifiedName()); + } + + TableAsField(final Table table, Name as) { + super(as, new RecordDataType<>( + ((AbstractTable) table).fieldsRow(), + (Class) table.getRecordType(), + table.getName() + )); + + this.table = table; + } + + @Override + final Table fields0() { + return table; + } + + @Override + final void acceptDefault(Context ctx) { + if (NO_NATIVE_SUPPORT.contains(ctx.dialect())) + ctx.data(DATA_LIST_ALREADY_INDENTED, true, c -> c.visit(new SelectFieldList<>(emulatedFields(ctx.configuration()).fields.fields))); + + + + + + + else + ctx.qualify(false, c -> c.visit(table)); + } + + @Override + public Field as(Name alias) { + return new TableAsField<>(table, alias); + } + + // ------------------------------------------------------------------------- + // XXX: Query Object Model + // ------------------------------------------------------------------------- + + @Override + public final Table $table() { + return table; + } + + + + + + + + + + + + + + +}