[jOOQ/jOOQ#4727] Let Table<R> extend SelectField<R>

This commit is contained in:
Lukas Eder 2022-02-21 15:27:44 +01:00
parent f5d1937238
commit 981411d2c1
3 changed files with 501 additions and 0 deletions

View File

@ -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<R extends Record> extends AbstractField<R> {
AbstractRowAsField(Name name, DataType<R> type) {
super(name, type);
}
abstract Fields fields0();
@SuppressWarnings("unchecked")
final AbstractRow<R> emulatedFields(Configuration configuration) {
return (AbstractRow<R>) 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<? super Context<?>> 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);
}

View File

@ -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<ROW extends Row, REC extends Record> extends AbstractRowAsField<REC> implements QOM.RowAsField<REC> {
static final Set<SQLDialect> 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<REC> as(Name alias) {
return new RowAsField<>(row, alias);
}
// -------------------------------------------------------------------------
// XXX: Query Object Model
// -------------------------------------------------------------------------
@Override
public final Row $row() {
return row;
}
}

View File

@ -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<R extends Record> extends AbstractRowAsField<R> implements QOM.TableAsField<R> {
static final Set<SQLDialect> NO_NATIVE_SUPPORT = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, IGNITE, MARIADB, MYSQL, SQLITE);
final Table<R> table;
TableAsField(Table<R> table) {
this(table, table.getQualifiedName());
}
TableAsField(final Table<R> table, Name as) {
super(as, new RecordDataType<>(
((AbstractTable<R>) table).fieldsRow(),
(Class<R>) table.getRecordType(),
table.getName()
));
this.table = table;
}
@Override
final Table<R> 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<R> as(Name alias) {
return new TableAsField<>(table, alias);
}
// -------------------------------------------------------------------------
// XXX: Query Object Model
// -------------------------------------------------------------------------
@Override
public final Table<R> $table() {
return table;
}
}